module libeventd.EvDns ;

import
	libeventd.Types;


alias	ubyte	u8;
alias	ushort	u16;
alias	uint		u32;
alias uint		ev_socklen_t ;


alias void* 	search_state;
alias void* 	evdns_callback_type ;
alias void* 	evdns_request_callback_fn_type;


const MAX_V4_ADDRS 		= 32 ;
const MAX_V6_ADDRS		= 32 ;
const HOST_NAME_MAX	= 255 ;

const _SS_PAD1SIZE		= 6 ;
const _SS_PAD2SIZE		=112 ;
	

alias ev_int sa_family_t;

struct in6_addr {
	ev_uint8_t[16] s6_addr;
}

struct sockaddr_in6 {
	sa_family_t		sin6_family;
	ev_uint16_t	sin6_port;
	in6_addr		sin6_addr;
}

struct sockaddr_storage {
	ev_short				ss_family;
	char[_SS_PAD1SIZE] 		__ss_pad1;
	long					__ss_align;
	char[_SS_PAD2SIZE]		__ss_pad2;
}

extern(C):

static in6_addr in6addr_any ;

struct evdns_request {
	request*		current_req;
	ev_int 		search_index;
	search_state*	__search_state;
	char*		search_origname;	/* needs to be free()ed */
	ev_int		search_flags;
}

struct request {
	u8*			__request; 		/* the dns packet data */
	u8			request_type;	/* TYPE_PTR or TYPE_A or TYPE_AAAA */
	ev_uint		request_len;
	ev_int		reissue_count;
	ev_int		tx_count;  /* the number of times that this packet has been sent */
	void*		user_pointer;  /* the pointer given to us for this request */
	evdns_callback_type	user_callback;
	nameserver*		ns;	/* the server which we last sent it */

	request* 		next, prev;
	
	event		timeout_event;
	u16			trans_id;  /* the transaction id */
	ev_uint		__request_appended ;	// :1		/* true if the request pointer is data which follows this struct */
	ev_uint		__transmit_me ;		//:1	/* needs to be transmitted */

	/* XXXX This is a horrible hack. */
	char**		put_cname_in_ptr; /* store the cname here if we get one. */
	evdns_base*	base;
	evdns_request*	handle;
}

struct reply {
	ev_uint type;
	ev_uint __have_answer; // : 1
	union {
		struct {
			u32					__addrcount_4;
			u32[MAX_V4_ADDRS]		__addresses_4;
		} // a;
		struct {
			u32					__addrcount_6;
			in6_addr[MAX_V6_ADDRS]	__addresses_6;
		} //aaaa;
		struct {
			char[HOST_NAME_MAX]	__name;
		} // ptr;
	} // data;
}

struct nameserver {
	evutil_socket_t		socket;	 /* a connected UDP socket */
	sockaddr_storage		address;
	ev_socklen_t		addrlen;
	ev_int			failed_times;  /* number of times which we have given this server a chance */
	ev_int			timedout;  /* number of times in a row a request has timed out */
	event			__event;
	nameserver*		next, prev;
	event			timeout_event;  /* used to keep the timeout for */
	
	evdns_request*		probe_request;
	char				state;  /* zero if we think that this server is down */
	char				choked;  /* true if we have an EAGAIN from this server's socket */
	char				write_waiting;  /* true if we are waiting for EV_WRITE events */
	evdns_base*		base;
}


struct evdns_server_port {
	evutil_socket_t	socket; /* socket we use to read queries and write replies. */
	int			refcnt; /* reference count. */
	char			choked; /* Are we currently blocked from writing? */
	char			closing; /* Are we trying to close this port, pending writes? */
	evdns_request_callback_fn_type	user_callback; /* Fn to handle requests */
	void*		user_data; /* Opaque pointer passed to user_callback */
	event		__event;
	server_request*	pending_replies;
	event_base*	__event_base;

version(_EVENT_DISABLE_THREAD_SUPPORT){}else{
	void*	lock;
}

}

/* Represents part of a reply being built.	(That is, a single RR.) */
struct server_reply_item {
	server_reply_item*	next; /* next item in sequence. */
	char*			name; /* name part of the RR */
	u16				type; /* The RR type */
	u16				__class; /* The RR class (usually CLASS_INET) */
	u32				ttl; /* The RR TTL */
	char				is_name; /* True iff data is a label */
	u16				datalen; /* Length of data; -1 if data is a label */
	void*			data; /* The contents of the RR */
}

struct server_request {
	server_request*		next_pending;
	server_request*		prev_pending;

	u16				trans_id; /* Transaction id. */
	evdns_server_port*	port; /* Which port received this request on? */
	sockaddr_storage		addr; /* Where to send the response */
	ev_socklen_t		addrlen; /* length of addr */

	ev_int			n_answer; /* how many answer RRs have been set? */
	ev_int			n_authority; /* how many authority RRs have been set? */
	ev_int			n_additional; /* how many additional RRs have been set? */

	server_reply_item*	answer; /* linked list of answer RRs */
	server_reply_item*	authority; /* linked list of authority RRs */
	server_reply_item*	additional; /* linked list of additional RRs */

	char*			response;
	size_t			response_len;
	evdns_server_request	base;
}



struct evdns_base {
	request**		req_heads;
	request*		req_waiting_head;
	nameserver*	server_head;
	int			n_req_heads;

	event_base*	__event_base;

	int			global_good_nameservers;

	int			global_requests_inflight;
	int			global_requests_waiting;

	int			global_max_requests_inflight;

	timeval		global_timeout;	/* 5 seconds by default */
	int			global_max_reissues;  /* a reissue occurs when we get some errors from the server */
	int			global_max_retransmits;  /* number of times we'll retransmit a request which timed out */
	
	int			global_max_nameserver_timeout;
	/* true iff we will use the 0x20 hack to prevent poisoning attacks. */
	int			global_randomize_case;

	/* The first time that a nameserver fails, how long do we wait before
	 * probing to see if it has returned?  */
	timeval 		global_nameserver_probe_initial_timeout;

	/** Port to bind to for outgoing DNS packets. */
	sockaddr_storage	global_outgoing_address;
	/** ev_socklen_t for global_outgoing_address. 0 if it isn't set. */
	ev_socklen_t	global_outgoing_addrlen;

	timeval		global_getaddrinfo_allow_skew;

	ev_int		getaddrinfo_ipv4_timeouts;
	ev_int		getaddrinfo_ipv6_timeouts;
	ev_int		getaddrinfo_ipv4_answered;
	ev_int		getaddrinfo_ipv6_answered;

	search_state*	global_search_state;

	hosts_list		hostsdb;
	
	version(_EVENT_DISABLE_THREAD_SUPPORT){}else{
		void *lock;
	}
}

struct hosts_entry {
	TAILQ_ENTRY!(hosts_entry)	next;
	union {
		sockaddr		sa;
		sockaddr_in	sin;
		sockaddr_in6	sin6;
	} // addr;
	int		addrlen;
	char[1]	hostname;
}

struct hosts_list {
	mixin TAILQ_HEAD!(hosts_entry);
}

